#include "yuv_to_jpeg.h"
#include <stdio.h>
#include <linux/videodev2.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> 
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <poll.h>
#include <stdlib.h>
#include "video_server.h"
#include <time.h>
#include <jpeglib.h>
#include <jerror.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>

#define VIDEO_DEV	"/dev/video0" //摄像头设备节点
#define BMP_FILE "image/"//图片保存路径
#define VIDEO_BUFF_NUM 2 //内存映射个数
#define ASCII_NUM  16

void yuv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight);
void Add_Watermark(char *src,char *str,const unsigned char ascii[][16],unsigned int x,unsigned int y,unsigned int size,unsigned int width,unsigned int height,unsigned int colour);
int Video_Init(void);
int SaveJPGImage(unsigned char *rgb,int width,int height,char *file_name);
void *video_get(void *arg);
int HTTP_Send(int clientfd, char *filename, char *type, char *buff);
int HTTP_Send_Live(int clientfd,char *buff);

int serverfd;//服务器套接字
pthread_mutex_t video_mutex = PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥锁
pthread_cond_t video_cond = PTHREAD_COND_INITIALIZER;//静态初始化条件变量
int video_fd;//摄像头描述符
int video_width;//图像宽度
int video_height;//图像高度
char *viodeo_mmapbuff[VIDEO_BUFF_NUM];//结构体内存映射地址数组
char *video_jpegbuff;//jpeg图片文件
unsigned int video_jpeg_size;
char userName[]="777";
char passWord[]="123456";



/*线程清理函数*/
void pthread_clear(void *arg)
{
    int clientd=*(int *)arg;
    close(clientd);
    printf("线程%d已完成清理\n",clientd);
}
///////////////////////////////////////////////////////////////////////////////////////
/*子线程*/
void *routine(void *arg) {

    int clientfd=*(int *)arg;
    free(arg);
    pthread_cleanup_push(pthread_clear,&clientfd); //注册清理函数
    int rb;

    char buff[1024];
    rb=read(clientfd, buff, sizeof(buff)-1);
    buff[rb]='\0';
    printf("resive:%s-----------------------------------------------------------------\n", buff);
    int res;
    //监测客户端的读写事件(是否断开连接)
    fd_set readfds;
    struct timeval tv;
    
    if(strstr(buff, "GET / HTTP/1.1")) {//发送配置数据
        res=HTTP_Send(clientfd, "server_file/login.html", "text/html", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    else if(strstr(buff, "GET /1.bmp HTTP/1.1")) {//建立长链接
        res=HTTP_Send_Live(clientfd,buff);
       // res=HTTP_Send(clientfd, "server_file/8.bmp", "application/x-bmp", buff);
    }
    else if(strstr(buff, "GET /style.css HTTP/1.1")) {//.css
        res=HTTP_Send(clientfd, "server_file/style.css", "text/css", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    else if(strstr(buff, "GET /logo.png HTTP/1.1")) {//logo
        res=HTTP_Send(clientfd, "server_file/2021.7.2.9.bmp", "application/x-bmp", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    else if(strstr(buff, "GET /three.min.js")) {//three.min.js
        res=HTTP_Send(clientfd, "server_file/three.min.js", "application/x-javascript", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    else if(strstr(buff, "GET /Stats.min.js")) {//Stats.min.js
        res=HTTP_Send(clientfd, "server_file/Stats.min.js", "application/x-javascript", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    else if(strstr(buff, "GET /favicon.ico HTTP/1.1")) {//.ico
        res=HTTP_Send(clientfd, "server_file/logo.ico", "image/x-icon", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    else if(strstr(buff, "POST / HTTP/1.1")) {//.ico
        char name[100];
        char password[100];
        strcpy(name,strstr(buff,"userName=")+strlen("userName="));
        *strstr(name,"&")='\0';
        strcpy(password,strstr(buff,"passWord=")+strlen("passWord="));

        if(strcmp(name,userName)==0)
        {
            if(strcmp(password,passWord)==0)
            {
                res=HTTP_Send_Live(clientfd,buff);//建立长链接
            }
        }
        else
            res=HTTP_Send(clientfd, "server_file/蓝环.png", "image/png", buff);
        printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }

    pthread_exit(NULL); //线程结束
    pthread_cleanup_pop(1);
}

int main(int argc, char **argv) {
    if(argc !=2) {
        printf("./a.out <端口号>\n");
        return 0;
    }

    signal(SIGPIPE, SIG_IGN); //忽略PIPE信号  客户端关闭信号，不处理会使进程关闭
    /*1.创建套接字*/
    serverfd=socket(AF_INET, SOCK_STREAM, 0);
    if(serverfd==-1) {
        printf("s:服务器套接字创建失败\n");
        return 0;
    }

    /*绑定端口号*/
    struct sockaddr_in server_addr;
    server_addr.sin_family=AF_INET; //IPV4
    server_addr.sin_port=htons(atoi(argv[1])); //端口号
    //server_addr.sin_addr.s_addr=inet_addr("192.168.2.190");//字符串IP转换整数
    server_addr.sin_addr.s_addr=INADDR_ANY; //本地所有IP

    if(bind(serverfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in))) {
        printf("s:服务器绑定端口号失败\n");
        close(serverfd);
        return 0;
    }

    /*3.设置监听数量*/
    if(listen(serverfd, 10)) {
        printf("s:服务器设置监听数量失败\n");
        close(serverfd);
        return 0;
    }

    struct sockaddr_in client_addr;
    int len=sizeof(struct sockaddr);
    int clientfd;
    pthread_t pthid;
    int *clientfd_p;
    /*摄像头获取子线程*/
    pthread_create(&pthid, NULL, video_get,NULL); //创建子线程
    while(1) {
        /*4.等待客户端连接*/
        clientfd=accept(serverfd, (struct sockaddr*)&client_addr, &len);
        if(clientfd==-1) {
            printf("s:创建客户端套接字失败\n");
        }
        else {
            clientfd_p=malloc(sizeof(int));
            *clientfd_p=clientfd;
            printf("s：客户端连接成功,ip:%s,port:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
            pthread_create(&pthid, NULL, routine, clientfd_p); //创建子线程
            pthread_detach(pthid); //设置分离属性
        }
    }
    pthread_cond_destroy(&video_cond);//销毁条件变量
    pthread_mutex_destroy(&video_mutex);//销毁锁
    return 0;


}
/*HTTP发送一帧数据*/
int HTTP_Send(int clientfd, char *filename, char *type, char *buff) {
    int filefd=open(filename, O_RDONLY);
    struct stat stat;
    fstat(filefd, &stat);

    if(stat.st_size<=0) {
        printf("文件大小为0\n");
        close(filefd);
        return -1;
    }

    sprintf(buff, "HTTP/1.1 200 OK\r\nContent-type:%s\r\nContent-Length:%d\r\n\r\n", type, stat.st_size);
    printf("Sended:%s>>>\n", buff);
    int wb=write(clientfd, buff, strlen(buff));

    if(wb !=strlen(buff)) {
        printf("发送报头失败\n");
        close(filefd);
        return -2;
    }
    int rb;

    while(1) {
        rb=read(filefd, buff, 1024);
        wb=write(clientfd, buff, rb);
        if(wb !=rb) {
            close(filefd);
            return -3;
        }

        if(rb !=1024) break;
    }
    close(filefd);
    return 0;
}
/*长连接发送jpeg数据流*/
int HTTP_Send_Live(int clientfd,char *buff)
{
        /*
        multipart 表示需要长连接，
        x-mixed-replace 表示使用后来的数据替换之前的数据，
        boundary 表示每个数据块的边界，正如其名。
        而 boundarydonotcross 就是边界符了，
        一个数据块以 boundarydonotcross 字符串为结尾。*/
        sprintf(buff, "HTTP/1.0 200 OK\r\n"\
                    "Server: wbyq\r\n"\
                    "Content-Type: multipart/x-mixed-replace;boundary=""boundarydonotcross""\r\n"\
                    "\r\n"\
                    "--""boundarydonotcross""\r\n");
        write(clientfd, buff, strlen(buff)); //向浏览器发送
        int filefd;
        char *jpg_buff=malloc(video_width*video_height*3);
        unsigned int jpg_size;
        fd_set readfds;
	    struct timeval tv;
        printf("与客户端%d建立长连接\n");
        //循环发送流数据
        while(1)
        {
            //等待条件变量,抢锁 等待摄像头数据采集完毕
            pthread_mutex_lock(&video_mutex);//上锁
            pthread_cond_wait(&video_cond,&video_mutex);//等待条件变量
            jpg_size=video_jpeg_size;
            memcpy(jpg_buff,video_jpegbuff,jpg_size);//拷贝JPG图像数据
            pthread_mutex_unlock(&video_mutex);//互斥锁解锁

            /*监听客户端是否断开*/
            tv.tv_sec=0;
            tv.tv_usec=0;
            FD_ZERO(&readfds);
            FD_SET(clientfd, &readfds);
            /*检测文件描述符是否发生了读写事件*/
            if(select(clientfd+1, &readfds, NULL, NULL, &tv)==1) {
                if(read(clientfd, buff, 1)<=0) {
                    printf("客户端%d已断开-------------\n",clientfd);
                    free(jpg_buff); //释放空间
                    break;
                }
            }


            sprintf(buff, "Content-Type: image/jpeg\r\n"\
                            "Content-Length: %d\r\n"\
                            "\r\n", jpg_size);
            write(clientfd, buff, strlen(buff)); //向浏览器发送报文
            write(clientfd, jpg_buff, jpg_size); //发送 MJPEG 数据
            sprintf(buff, "\r\n--""boundarydonotcross""\r\n"); //间隔符号
            write(clientfd, buff, strlen(buff)); //向浏览器发送
            printf("已向%d发送一帧数据\n",clientfd);
        }
        return 0;
}
/*获取摄像头数据*/
void *video_get(void *arg)
{
    printf("VideoInit_res=%d\n",Video_Init());
    video_jpegbuff=malloc(video_height*video_width*3);
	
	struct pollfd fds;
	fds.fd=video_fd;
	fds.events=POLLIN;
	fds.revents=0;
	struct v4l2_buffer video_buff;
	//time_t sec;//获取系统秒单位时间
	while(1)
	{
        memset(&video_buff,0,sizeof(struct v4l2_buffer));
		/*监测读事件*/
		poll(&fds,1,-1);//永久等待
		/*1.从队列中读*/
		video_buff.memory=V4L2_MEMORY_MMAP;//内存映射
		video_buff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2视频捕获
		if(ioctl(video_fd,VIDIOC_DQBUF,&video_buff))
            {
                printf("------------------------从队列中读失败------\n");
                pthread_exit(0); //线程结束
            }
		//printf("buff[%d]:%#x\n",video_buff.index,viodeo_mmapbuff[video_buff.index]);
		
		/*YUYV转jpeg*/
        pthread_mutex_lock(&video_mutex);//互斥锁上锁
 	    video_jpeg_size=yuv_to_jpeg(video_width,video_height,video_width*video_height*3,viodeo_mmapbuff[video_buff.index],video_jpegbuff,80);
        pthread_cond_broadcast(&video_cond);//广播唤醒
		pthread_mutex_unlock(&video_mutex);//互斥锁解锁
       // printf("Camera Capture a picture\n");
		
		/*3.将缓冲区重新添加到队列*/
		if(ioctl(video_fd,VIDIOC_QBUF,&video_buff))
        {
            printf("------------------------将缓冲区重新添加到队列失败------\n");
                break;
        }
	}
	pthread_exit(NULL); //线程结束

}
/*摄像头初始化*/
int Video_Init(void)
{
	/*1.打开摄像头设备*/
	video_fd=open(VIDEO_DEV,O_RDWR);
	if(video_fd==-1)return -1;
	/*2.设置图像格式*/
	struct v4l2_format video_format;//设置图像格式
	memset(&video_format,0,sizeof(struct v4l2_format));
	video_format.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获
	video_format.fmt.pix.width=1920;//最大分辨率
	video_format.fmt.pix.height=1080;
	video_format.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;//YUYV模式
	if(ioctl(video_fd, VIDIOC_S_FMT,&video_format))return -2;
	video_width=video_format.fmt.pix.width;//实际分辨率
	video_height=video_format.fmt.pix.height;
	printf("图像大小:%d * %d\n",video_width,video_height);
	/*3.申请缓冲区*/
	struct v4l2_requestbuffers video_requestbuffers;//申请缓冲区
	memset(&video_requestbuffers,0,sizeof(struct v4l2_requestbuffers));
	video_requestbuffers.count=VIDEO_BUFF_NUM;//缓冲区数量
	video_requestbuffers.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2视频捕获
	video_requestbuffers.memory=V4L2_MEMORY_MMAP;//内存映射
	if(ioctl(video_fd, VIDIOC_REQBUFS,&video_requestbuffers))return -3;
	printf("申请的缓冲区个数:%d\n",video_requestbuffers.count);
	/*4.将缓冲区映射到进程空间*/
	struct v4l2_buffer video_buffer;
	int i;
	for(i=0;i<VIDEO_BUFF_NUM;i+=1)
	{
		memset(&video_buffer,0,sizeof(struct v4l2_buffer));
		video_buffer.index=i;//下标
		video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2视频捕获
		video_buffer.memory=V4L2_MEMORY_MMAP;//内存映射
		if(ioctl(video_fd, VIDIOC_QUERYBUF,&video_buffer))return -4;
		viodeo_mmapbuff[i]=mmap(NULL, video_buffer.length, PROT_READ| PROT_WRITE, MAP_SHARED,video_fd, video_buffer.m.offset);
	}
	/*5.将缓冲区添加到队列*/
	for(i=0;i<VIDEO_BUFF_NUM;i+=1)
	{
		memset(&video_buffer,0,sizeof(struct v4l2_buffer));
		video_buffer.index=i;//下标
		video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2视频捕获
		video_buffer.memory=V4L2_MEMORY_MMAP;//内存映射
		if(ioctl(video_fd, VIDIOC_QBUF,&video_buffer))return -5;
	}
	/*6.开启摄像头数据采集*/
	int type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
	if(ioctl(video_fd,VIDIOC_STREAMON,&type))return -6;

	return 0;
	
}


/*添加水印(正常rgb)*/
/*
	形参:	<src> -- rgb图片数组
			<str> -- 要显示的字符串
			<ascii> -- 取模数组
			<x> -- 显示的起始坐标
			<y> -- 显示的起始坐标
			<size> -- 字符大小
			<width> -- 图片宽度
			<height> -- 图片高度
tips:取模方式为逐列式高位在前
*/
void Add_Watermark(char *src,char *str,const unsigned char ascii[][16],unsigned int x,unsigned int y,unsigned int size,unsigned int width,unsigned int height,unsigned int colour)
{
	int num;//记录显示的字符在取模中的个数
	unsigned int y0=y;
	unsigned int i=0;
	unsigned char j=0,data=0;
	while(*str)
	{
		if(*str<' ' || *str>'~')
		{
			str+=1;
			continue;
		}	
		num=*str-' ';
		for(i=0;i<size*size/16;i++) //遍历整个字符取模
		{
			data=ascii[num][i];
			for(j=0;j<8;j++)
			{
				if(data&0x80)
				{
					*(src+y0*width*3+x*3)=(colour>>0)&0xff;
					*(src+y0*width*3+x*3+1)=(colour>>8)&0xff;
					*(src+y0*width*3+x*3+2)=(colour>>16)&0xff;
				}
				data<<=1;
				y0++;
			}
			if(y0-y==size)
			{
				y0=y;
				x++;
			}
		}
		str+=1;
	}
	
}

/*YUYV转RGB888*/
void yuv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
	int x;
	int z=0;
	unsigned char *ptr = rgb_buffer;
	unsigned char *yuyv= yuv_buffer;
	for (x = 0; x < iWidth*iHeight; x++)
	{
		int r, g, b;
		int y, u, v;
		if (!z)
		y = yuyv[0] << 8;
		else
		y = yuyv[2] << 8;
		u = yuyv[1] - 128;
		v = yuyv[3] - 128;
		r = (y + (359 * v)) >> 8;
		g = (y - (88 * u) - (183 * v)) >> 8;
		b = (y + (454 * u)) >> 8;
		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
		if(z++)
		{
			z = 0;
			yuyv += 4;
		}
	}
}
/*rgb颜色转jpeg格式保存*/
/*jpeg压缩函数
 *@rgb：指向存放rgb24数据的起始地址
 *@width：屏幕（分辨率）的宽度
 *@height：屏幕（分辨率）的高度
 */
 int SaveJPGImage(unsigned char *rgb,int width,int height,char *file_name)
 {
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	FILE * outfile;
	JSAMPROW row_pointer[1];
	int row_stride;
	memset(&cinfo,0,sizeof(struct jpeg_compress_struct));
	/*1. 注册错误处理方式*/
    cinfo.err = jpeg_std_error(&jerr); 

	/*2. 创建jpeg压缩对象*/
    jpeg_create_compress(&cinfo);

     if ((outfile = fopen(file_name, "w+b")) == NULL)
     {
         printf("文件创建失败.jpg\n");
         return -1;
     }
    
	 /*3. 指定压缩后图像存放的目标文件*/
     jpeg_stdio_dest(&cinfo,outfile);
	 /*4. 配置输出的JPEG图像的参数信息*/
     cinfo.image_width = width;      //宽
     cinfo.image_height = height;    //高
     cinfo.input_components = 3;     //像素（3表示彩色）
     cinfo.in_color_space = JCS_RGB; //输入数据格式为RGB(彩色)
	  /*5. 设置默认JPEG参数*/
     jpeg_set_defaults(&cinfo);
	 /*6. 配置JPEG图像压缩质量为80，可填范围10~100*/
     jpeg_set_quality(&cinfo,80,TRUE);
	 /*7. 开始压缩*/
     jpeg_start_compress(&cinfo,TRUE);
	 row_stride = width * 3; //一行的字节数 
	 /*8. 一行一行进行压缩*/
     while(cinfo.next_scanline < cinfo.image_height)
     {
		 //得到每一行的数据地址
         row_pointer[0] = &rgb[cinfo.next_scanline * row_stride];
		 //将压缩后的数据写入到文件
         jpeg_write_scanlines(&cinfo,row_pointer,1);
     }	 
	 /*9. 结束解压缩操作*/
     jpeg_finish_compress(&cinfo);
	 /*10. 关闭文件*/
     fclose(outfile);
	 /*11. 销毁压缩使用的结构体，释放空间*/
     jpeg_destroy_compress(&cinfo);
     return 0;	     
 }

